﻿using Moki;
using Moki.Tests.Model;
using NUnit.Framework;
using System;

namespace Moki.Specification
{
    [TestFixture]
    public class StubSpec
    {
        [TearDown]
        public void TearDown()
        {
            Mock.CleanUp();
        }

        #region When.Called(...).Return(...)
        [Test]
        public void Stub_call_with_arguments()
        {
            ITests test = Mock.Create<ITests>();

            Mock.WhenCalled(() => test.NeedSomeArguments(5, "aaa", true)).Returns(1);
            Mock.WhenCalled(() => test.NeedSomeArguments(-5, "aaa", true)).Returns(2);
            Mock.WhenCalled(() => test.NeedSomeArguments(10, "bb", false)).Returns(3);

            Assert.AreEqual(1, test.NeedSomeArguments(5, "aaa", true));
            Assert.AreEqual(2, test.NeedSomeArguments(-5, "aaa", true));
            Assert.AreEqual(3, test.NeedSomeArguments(10, "bb", false));
        }

        #endregion

        #region When.Called(...).Do(...)
        [Test]
        public void Stub_call_do()
        {
            ITests test = Mock.Create<ITests>();
            Mock.WhenCalled(() => test.GetAnswer()).Do(() => true);

            Assert.AreEqual(true, test.GetAnswer());

        }

        [Test]
        public void Stub_call_do_throws_exception()
        {
            ITests test = Mock.Create<ITests>();
            Mock.WhenCalled(() => test.GetAnswer()).Do(() => { throw new ArgumentNullException(); });

            Assert.Throws<ArgumentNullException>(() => test.GetAnswer());
        }

        [Test]
        public void Stub_call_do_complex_with_args()
        {
            ITests test = Mock.Create<ITests>();

            Mock.WhenCalled(() => test.Substring(2, "abc")).Do(args => { return (args[1] as string).Substring((int)args[0]); });
            Mock.WhenCalled(() => test.Substring(0, "abc")).Do(args => { return (args[1] as string).Substring((int)args[0]); });

            Assert.AreEqual("c", test.Substring(2, "abc"));
            Assert.AreEqual("abc", test.Substring(0, "abc"));

        }

        [Test]
        public void Stub_call_do_on_partial_mock()
        {
            PartialMockTest test = Mock.Create<PartialMockTest>();

            Mock.WhenCalled(() => test.GetSomethingWithArgs(1)).Do(args => { return "dont do this!"; });

            Assert.AreEqual("dont do this!", test.CallGetSomethingWithArgs(1));
            Assert.AreEqual("2", test.CallGetSomethingWithArgs(2));

        }

        [Test]
        public void Stub_call_do_works_only_for_specified_args()
        {
            ITests test = Mock.Create<ITests>();

            Mock.WhenCalled(() => test.Substring(2, "abc")).Do(args => { return (args[1] as string).Substring((int)args[0]); });
            Mock.WhenCalled(() => test.Substring(4, "abc")).Returns("");

            Assert.AreEqual("c", test.Substring(2, "abc"));
            Assert.AreEqual("", test.Substring(4, "abc"));
            Assert.AreEqual(null, test.Substring(1, "abc")); // this was not stubbed;

        } 
        #endregion

        #region When.Called(...).Throw(...)

        [Test]
        public void Stub_call_throw_exception_for_no_args()
        {
            ITests test = Mock.Create<ITests>();
            Mock.WhenCalled(() => test.GetSomething()).Throws(new NullReferenceException());
            Assert.Throws<NullReferenceException>(() => test.GetSomething());
        }

        [Test]
        public void Stub_call_throw_exception_for_args()
        {
            ITests test = Mock.Create<ITests>();

            Mock.WhenCalled(() => test.NeedSomeArguments(1, "a", true)).Throws(new ArgumentException());
            Mock.WhenCalled(() => test.NeedSomeArguments(1, "b", true)).Returns(1);

            Assert.AreEqual(1, test.NeedSomeArguments(1, "b", true));
            Assert.Throws<ArgumentException>(() => test.NeedSomeArguments(1, "a", true));

        }

        #endregion

        #region When.Called(...).CallBack(...)

        [Test]
        public void Stub_call_callback_after_return()
        {
            ITests test = Mock.Create<ITests>();

            int i = 1;

            Mock.WhenCalled(() => test.NeedSomeArguments(i, "b", true)).Returns(1).Callback(args => i++);
            
            Assert.AreEqual(1, test.NeedSomeArguments(1, "b", true));
            Assert.AreEqual(1, test.NeedSomeArguments(1, "b", true));
            Assert.AreEqual(3, i);
          
        }

        [Test]
        public void Stub_call_callback_change_args()
        {
            ITests test = Mock.Create<ITests>();

            int i = 1;
            string b = "b";

            Mock.WhenCalled(() => test.NeedSomeArguments(1, b, true)).Returns(i).Callback(args => { args[0] = 2; args[1] += "b"; });

            Assert.AreEqual(1, test.NeedSomeArguments(1, "b", true));
            Assert.AreEqual(1, test.NeedSomeArguments(2, "bb", true));
        }


        [Test]
        public void Stub_call_callback_without_return()
        {
            ITests test = Mock.Create<ITests>();

            int i = 1;

            Mock.WhenCalled(() => test.DoSomethingWithArgs(i, true)).Callback(args => i++);

            test.DoSomethingWithArgs(1, true);
            test.DoSomethingWithArgs(1, true);

            Assert.AreEqual(3, i);

        }
        #endregion
    }
}
